﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyOnlineScheduling
{
    class ProcessorHeapIndexed
    {
        public int processorId;

        public ProcessorHeapIndexed(int i)
        {
            processorId = i;
        }
    }

    class ProcessorWrap
    {
        public int minHeapId;
        public int maxHeapId;

        public ProcessorWrap(int i)
        {
            minHeapId = i;
            maxHeapId = i;
        }
    }
    
    class ProcessorDualHeap
    {
        ProcessorHeapIndexed[] maxProcessorHeap;
        ProcessorHeapIndexed[] minProcessorHeap;
        ProcessorWrap[] heapProcessorIndexes;

        public int indexOfMax()
        {
            return maxProcessorHeap[0].processorId;
        }

        public int indexOfMin()
        {
            return minProcessorHeap[0].processorId;
        }

        public int getMinHeapIndex(int maxHeapIndex)
        {
            int realIndex;
            int minHeapIndex;

            realIndex = maxProcessorHeap[maxHeapIndex].processorId;
            minHeapIndex = heapProcessorIndexes[realIndex].minHeapId;

            return minHeapIndex;
        }

        public int getMinHeapIndexFromRealIndex(int realIndex)
        {
            return heapProcessorIndexes[realIndex].minHeapId;
        }

        public int getMaxHeapIndex(int minHeapIndex)
        {
            int realIndex;
            int maxHeapIndex;

            realIndex = minProcessorHeap[minHeapIndex].processorId;
            maxHeapIndex = heapProcessorIndexes[realIndex].maxHeapId;

            return maxHeapIndex;        
        }

        public ProcessorDualHeap(int numberOfProcessors)
        {
            maxProcessorHeap = new ProcessorHeapIndexed[numberOfProcessors];
            minProcessorHeap = new ProcessorHeapIndexed[numberOfProcessors];
            heapProcessorIndexes = new ProcessorWrap[numberOfProcessors];

            for (int i = 0; i < numberOfProcessors; i++)
            {
                maxProcessorHeap[i] = new ProcessorHeapIndexed(i);
                minProcessorHeap[i] = new ProcessorHeapIndexed(i);
                heapProcessorIndexes[i] = new ProcessorWrap(i);
            }
        }

        public bool maxHeapify(Processor[] processors, int i, int currentTime)
        {
            if (i >= maxProcessorHeap.Length) return false;
            
            int l = left(i);
            int r = right(i);
            int largest = i;

            processors[maxProcessorHeap[i].processorId].calculateWeightedTimeRemaining(currentTime);
            double weightedTimeRemainingI = processors[maxProcessorHeap[i].processorId].weightedTimeRemaining;
            double weightedTimeRemainingLargest = weightedTimeRemainingI;

            double weightedTimeRemainingL;
            double weightedTimeRemainingR;

            if (l < maxProcessorHeap.Length)
            {
                processors[maxProcessorHeap[l].processorId].calculateWeightedTimeRemaining(currentTime);
                weightedTimeRemainingL = processors[maxProcessorHeap[l].processorId].weightedTimeRemaining;
                if (weightedTimeRemainingL > weightedTimeRemainingLargest)
                {
                    largest = l;
                    weightedTimeRemainingLargest = weightedTimeRemainingL;
                }
            }

            if (r < maxProcessorHeap.Length)
            {
                processors[maxProcessorHeap[r].processorId].calculateWeightedTimeRemaining(currentTime);
                weightedTimeRemainingR = processors[maxProcessorHeap[r].processorId].weightedTimeRemaining;
                if (weightedTimeRemainingR > weightedTimeRemainingLargest)
                    largest = r;
            }

            //int lastIndex = processors[maxProcessorHeap[i].processorId].taskList.Count - 1;
            //int arrivalTime = processors[maxProcessorHeap[i].processorId].taskList[lastIndex].arrivalTime;

            if (largest != i)
            {
                maxSwap(i, largest);
                maxHeapify(processors, largest, currentTime);
            }
            else return false;

            return true;
        }

        public bool minHeapUp(Processor[] processors, int i)
        {
            int p = parent(i);
            if (i <= 0) return false;

            int iTime = processors[minProcessorHeap[i].processorId].endTime;
            int parentTime = processors[minProcessorHeap[p].processorId].endTime;
            if (iTime < parentTime)
            {
                minSwap(i, p);
                minHeapUp(processors, p);
            }

            return true;
        }

        public bool maxHeapUp(Processor[] processors, int i, int currentTime)
        {
            if (i == 0) return false;
            
            int p = parent(i);
            
            processors[maxProcessorHeap[i].processorId].calculateWeightedTimeRemaining(currentTime);
            processors[maxProcessorHeap[p].processorId].calculateWeightedTimeRemaining(currentTime);
            double iWeightedRemainingTime = processors[maxProcessorHeap[i].processorId].weightedTimeRemaining;
            double parentWeightedRemainingTime = processors[maxProcessorHeap[p].processorId].weightedTimeRemaining;
            
            //int lastIndex = processors[maxProcessorHeap[i].processorId].taskList.Count - 1;
            //int arrivalTime = processors[maxProcessorHeap[i].processorId].taskList[lastIndex].arrivalTime;
            
            if (parentWeightedRemainingTime < iWeightedRemainingTime)
            {
                maxSwap(i, p);
                maxHeapUp(processors, p, currentTime);
            }

            return true;
        }

        public bool minHeapify(Processor[] processors, int i)
        {
            if (i >= minProcessorHeap.Length) return false;

            int l = left(i);
            int r = right(i);
            int smallest = i;
            int timeRemainingI = processors[minProcessorHeap[i].processorId].endTime;
            int timeRemainingSmallest = timeRemainingI;

            int timeRemainingL;
            int timeRemainingR;

            if (l < minProcessorHeap.Length)
            {
                timeRemainingL = processors[minProcessorHeap[l].processorId].endTime;
                if (timeRemainingL < timeRemainingSmallest)
                {
                    smallest = l;
                    timeRemainingSmallest = timeRemainingL;
                }
            }

            if (r < minProcessorHeap.Length)
            {
                timeRemainingR = processors[minProcessorHeap[r].processorId].endTime;
                if (timeRemainingR < timeRemainingSmallest)
                {
                    smallest = r;
                }
            }

            if (smallest != i)
            {
                minSwap(i, smallest);
                minHeapify(processors, smallest);
            }
            else return false;

            return true;
        }

        private int left(int i)
        {
            return (2 * (i + 1) - 1);
        }

        private int right(int i)
        {
            return (2 * (i + 1));
        }

        private int parent(int i)
        {
            return ((i - 1) / 2);
        }

        private void maxSwap(int first, int second)
        {
            int temp = maxProcessorHeap[first].processorId;
            maxProcessorHeap[first].processorId = maxProcessorHeap[second].processorId;
            maxProcessorHeap[second].processorId = temp;

            heapProcessorIndexes[maxProcessorHeap[first].processorId].maxHeapId = first;
            heapProcessorIndexes[maxProcessorHeap[second].processorId].maxHeapId = second;
        }

        private void minSwap(int first, int second)
        {
            int temp = minProcessorHeap[first].processorId;
            minProcessorHeap[first].processorId = minProcessorHeap[second].processorId;
            minProcessorHeap[second].processorId = temp;

            heapProcessorIndexes[minProcessorHeap[first].processorId].minHeapId = first;
            heapProcessorIndexes[minProcessorHeap[second].processorId].minHeapId = second;
        }
    }
}
